package com.tjmc.finance.util;

import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

@Slf4j
public class AesUtil {

    static final int KEY_LENGTH_BYTE = 32;
    static final int TAG_LENGTH_BIT = 128;
    private final byte[] aesKey;

    public AesUtil(byte[] key) {
        if (key.length != KEY_LENGTH_BYTE) {
            throw new IllegalArgumentException("无效的ApiV3Key，长度必须为32个字节");
        }
        this.aesKey = key;
    }

    public String decryptToString(String associatedData, byte[] nonce, String ciphertext)
            throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            if (StringUtils.isNotBlank(associatedData)) {
                cipher.updateAAD(associatedData.getBytes());
            } else {
                cipher.updateAAD(new byte[0]);
            }
            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * 微信验证签名
     *
     * @param timestamp   时间戳
     * @param nonce       随机串
     * @param requestBody 请求主体
     * @param signature   签名信息
     * @return
     * @throws SignatureException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    public static boolean signCheck(String timestamp, String nonce,
                                    String requestBody, String signature)
            throws Exception {
        //构造验签名串
        String signatureStr = timestamp + "\n" + nonce + "\n" + JSONObject.toJSONString(requestBody) + "\n";
        // 加载SHA256withRSA签名器
        Signature signer = Signature.getInstance("SHA256withRSA");
        // 用微信平台公钥对签名器进行初始化（调上一节中的获取平台证书方法）
        String keyPath = System.getenv("WECHAT_PAY_PUBLIC_KEY_PATH");

        signer.initVerify(initValidateCertFromDir(keyPath));
        // 把我们构造的验签名串更新到签名器中
        log.info("验签名串：{}", signatureStr);
        signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        // 把请求头中微信服务器返回的签名用Base64解码 并使用签名器进行验证
        return signer.verify(SecureUtil.decode(signature));
    }

    /**
     * 微信签名
     *
     * @param appId       appID
     * @param timestamp   时间差
     * @param nonce       随机字符串
     * @param requestBody package
     * @return
     */
    public static String sign(String appId, String timestamp, String nonce, String requestBody, String keyPath) throws Exception {
        String signatureStr = appId + "\n" + timestamp + "\n" + nonce + "\n" + requestBody + "\n";
        Signature signer = Signature.getInstance("SHA256withRSA");
        signer.initSign(loadPrivateKeyFromFile(keyPath));
        signer.update(signatureStr.getBytes());
        byte[] signed = signer.sign();
        return Base64.getEncoder().encodeToString(signed);
    }

    public static Certificate initValidateCertFromDir(String crtPath) throws Exception {
        CertificateFactory cf;
        FileInputStream in = null;
        Certificate validateCert;
        try {
            cf = CertificateFactory.getInstance("X.509");
            in = new FileInputStream(crtPath);
            validateCert = cf.generateCertificate(in);
        } catch (CertificateException e) {
            throw e;
        } catch (FileNotFoundException e) {
            throw e;
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (IOException e) {
                    throw e;
                }
            }
        }
        return validateCert;
    }

    private static PrivateKey loadPrivateKeyFromFile(String keyStorePath) throws Exception {
        // 从文件中读取私钥
        byte[] keyBytes = Files.readAllBytes(Paths.get(keyStorePath));

        // 移除PEM格式的头部和尾部
        String privateKeyPEM = new String(keyBytes, StandardCharsets.UTF_8);
        privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----", "");
        privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", "");
        privateKeyPEM = privateKeyPEM.replace("\n", "");

        // 将Base64编码的内容转换为byte数组
        byte[] encoded = Base64.getDecoder().decode(privateKeyPEM);

        // 根据byte数组生成PrivateKey对象
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
        KeyFactory kf = KeyFactory.getInstance("RSA"); // 或者其他算法如DSA
        return kf.generatePrivate(keySpec);
    }
}

